<?php
// 在脚本最开头强制设置错误处理（覆盖所有可能的错误）
ini_set('display_errors', 'stderr'); // 直接输出到 stderr
ini_set('error_log', 'php://stderr');

require_once __DIR__.'/vendor/autoload.php';
require_once __DIR__.'/DB.php';
set_error_handler(function ($severity, $message, $file, $line) {
    throw new ErrorException($message, 0, $severity, $file, $line);
});
define('BASE_DIR', realpath(__DIR__ . '/../') . DIRECTORY_SEPARATOR);

use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Exception\ParseException;
use PhpOffice\PhpWord\TemplateProcessor;

// 主入口
const CONFIG_PATH = __DIR__.'/../config.yaml'; // 固定配置文件路径
const DEFAULT_CHARSET = 'utf8mb4'; // 默认字符集
$options = getopt('', ['task:']);
$taskId = isset($options['task']) ? intval($options['task']) : null;
if ($taskId) {
    try {
        // 提前加载配置并初始化数据库连接
        if (!file_exists(DocGenerator::CONFIG_PATH)) {
            throw new RuntimeException("配置文件不存在: " . DocGenerator::CONFIG_PATH);
        }
        $fullConfig = Yaml::parseFile(DocGenerator::CONFIG_PATH);
        $mysql = array_merge([
            'host' => 'localhost',
            'port' => 3306,
            'charset' => DocGenerator::DEFAULT_CHARSET
        ], $fullConfig['mysql']);
        $dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=%s',
            $mysql['host'], $mysql['port'], $mysql['database'], $mysql['charset']);
        $db = DB::instance($dsn, $mysql);
        // 获取任务并检查是否存在
        $task = $db->table('doc_gen_tasks')->where('id', $taskId)->first();
        if (!$task) {
            throw new RuntimeException("任务 ID: {$taskId} 不存在");
        }
        $generator = new DocGenerator($db, $fullConfig); // 依赖注入
        $generator->processTask($taskId);
    } catch (Throwable $e) {
        $errorMsg = "[PHP ERROR] " . $e->getMessage();
        error_log($errorMsg);
        // 尝试更新任务状态
        if (isset($db)) {
            try {
                $db->table('doc_gen_tasks')->where('id', $taskId)->update([
                    'status' => 'failed',
                    'error_message' => $errorMsg
                ]);
            } catch (Throwable $dbError) {
                error_log("[PHP ERROR] 更新任务状态失败: " . $dbError->getMessage());
            }
        }
        exit(1);
    }
} else {
    exit("Error: Task ID is required. Usage: php DocGenerator.php --task=123\n");
}

class DocGenerator {
    private $db;
    private $config;
    const CONFIG_PATH = __DIR__.'/../config.yaml'; // 固定配置文件路径
    const DEFAULT_CHARSET = 'utf8mb4'; // 默认字符集
    const ALLOWED_TEMPLATE_DIR = 'uploads/wordmb/'; //word模板位置
    
    // 依赖注入
    public function __construct($db, $config) {
        $this->db = $db;
        $this->config = $config;
    }
    
    // 入口方法
    public function processTask($taskId) {
        try {
            $this->db->beginTransaction();
            // 1. 获取任务基本信息
            $task = $this->db->table('doc_gen_tasks')->where('id', $taskId)->first();
            if (!$task) {
                throw new Exception("任务ID:{$taskId}不存在");
            }
            // 2. 获取关联项目配置
            $xmid = $task['xmid'];
            $bgsx = $this->db->table('form_schemas')->where('id', $xmid)->first();
            if (!$bgsx) {
                throw new Exception("关联项目ID:{$xmid}不存在");
            }
            // 3. 初始化任务状态
            $this->db->table('doc_gen_tasks')->where('id', $taskId)->update(['status'=>'processing','progress' => 0,'error_message'=>null]);
            // 4. 执行生成逻辑
            $overwrite = $task['overwrite'] ? $task['overwrite'] : '否';
            if(!$zd = $task['field']){
                throw new Exception('word字段不存在');
            }
            if(!$wjm = $task['wjm']){
                throw new Exception('文件名字段不存在');
            }
            $this->scword($bgsx, $taskId, $overwrite, $zd, $wjm);
            $this->db->commit();
        } catch (Throwable $e) {
            if ($this->db->inTransaction()) {
                $this->db->rollBack();
            }
            $errorMsg = "[PHP ERROR] " . $e->getMessage();
            $this->db->table('doc_gen_tasks')->where('id', $taskId)->update(['status' => 'failed','error_message' => $errorMsg]);
            $this->log($errorMsg, $e);
            exit(1);
        }
    }
    
    // 生成word
    private function scword(array $bgsx, $taskId, $overwrite, $zd, $wjm) {
        $tableName = $bgsx['tablename'];
        if($overwrite == '是') {
            $datas = $this->db->table($tableName)->get();
        }else{
            $datas = $this->db->table($tableName)->where($zd, null)->orWhere($zd, '')->get();
        }
        $dataCount = count($datas);
        if ($dataCount == 0) {
            $this->log("任务 {$taskId} 无有效数据");
            $this->updateProgress($taskId, 100); // 标记为100%完成
            $this->db->table('doc_gen_tasks')->where('id', $taskId)->update(['status' => 'completed']);
            return;
        }
        // 转换并验证模板路径
        $templatePath = $this->getValidImagePath($bgsx['wordmb']);
        // 最终文件检查
        if (!is_readable($templatePath)) {
            throw new Exception("模板文件不可读: " . $templatePath);
        }
        try {
            $index = 0;
            foreach ($datas as $data) {
                $template = new TemplateProcessor($templatePath);
                $name = $data[$wjm] ?? $wjm;
                $filename = sprintf("%s.docx", $name);
                $fileDir = 'uploads/word/'. $bgsx['tablename']. '/';
                if (!file_exists($fileDir)) {
                    mkdir($fileDir, 0755, true);
                }
                $filePath = $fileDir . $filename;
                // 模板替换逻辑
                $this->processTemplate($bgsx, $template, $data);
                // 保存文档
                $template->saveAs($filePath);
                // 更新数据记录
                $filePath = $this->config['server']['url'] . $filePath; //加上网址前缀
                $this->db->table($tableName)->where('id', $data['id'])->update([$zd => $filePath]);
                // 更新进度
                $progress = intval(++$index / $dataCount * 100);
                $this->updateProgress($taskId, $progress);
            }
        } catch (Exception $e) {
            throw $e;
        }
    }

    // 模板替换
    private function processTemplate(array $bgsx, TemplateProcessor $template, array $data) 
    {
        $typeHandlers = [
            'image'     => 'handleSingleImage',   // 单图
            'multiimage'=> 'handleMultipleImages',// 多图
            'muselect'  => 'handleMultiSelect',   // 多选下拉
            'checkbox'  => 'handleMultiSelect',   // 多选框
            'sutable'   => 'handleSubTable',      // 子表
            'default'   => 'handleTextValue'      // 默认文本
        ];
        $fields = json_decode($bgsx['fields'], true);
        foreach ($fields as $fieldName => $fieldConfig) {
            try {
                // 获取字段值
                $value = $data[$fieldName] ?? '';
                // 根据类型选择处理器
                $fieldType = $fieldConfig['type'] ?? 'default';
                $handler = $typeHandlers[$fieldType] ?? $typeHandlers['default'];
                // 动态调用处理方法
                $this->{$handler}($template, $fieldName, $value, $fieldConfig);
            } catch (Exception $e) {
                throw $e; // 向上抛出中断流程
            }
        }
    }
    
    // 验证并返回有效图片文件路径
    private function getValidImagePath($rawValue): ?string {
        $baseUrl = $this->config['server']['url'] ?? 'https://zp.kzhgzs.cn/';
        if(!$fullPath = str_replace($baseUrl, '', $rawValue)){
            return null;
        }
        return urldecode(BASE_DIR . $fullPath);
    }

    // 处理单张图片
    private function handleSingleImage(TemplateProcessor $template, string $fieldName, $value, array $config)
    {
        // 安全获取图片路径
        $imagePath = $this->getValidImagePath($value);
        if ($imagePath) {
            if (!file_exists($imagePath)) {
                throw new RuntimeException("图片无法读取: $imagePath");
            }else{
                // 配置处理参数
                $options = $config['options'] ? explode('/', $config['options']) : [120, 150];
                $width = $options[0] ?? 120;
                $height = $options[1] ?? 150;
                $template->setImageValue($fieldName, ['path' => $imagePath,'width' => $width,'height' => $height,'ratio' => true]);
            }
        } else {
            $template->setValue($fieldName, '');
        }
    }
    
    // 处理多图展示
    private function handleMultipleImages(TemplateProcessor $template, string $fieldName, $value, array $config)
    {
        $images = json_decode($value, true) ?: [];
        // 清空默认占位符
        $template->setValue($fieldName, '');
        $options = $config['options'] ? explode('/', $config['options']) : [120, 150];
        $width = $options[0] ?? 120;
        $height = $optons[1] ?? 150;
        // 插入多张图片（需要模板预置多个${fieldName_1}等占位符）
        foreach ($images as $index => $image) {
            $placeholder = "{$fieldName}_" . ($index + 1);
            $imagePath = $this->getValidImagePath($image);
            if ($imagePath) {
                $template->setImageValue($placeholder, ['path' => $imagePath,'width' => $width,'height' => $height]);
            }
        }
    }
    
    // 处理多选/复选框
    private function handleMultiSelect(TemplateProcessor $template, string $fieldName, $value, array $config)
    {
        $options = json_decode($value, true) ?: [];
        $template->setValue($fieldName, implode('、', $options));
    }
    
    // 处理子表数据
    private function handleSubTable(TemplateProcessor $template, string $fieldName, $value, array $config)
    {
        $rows = json_decode($value, true) ?: [];
        if (!is_array($rows) || empty($rows)) {
            $template->setValue($fieldName, '');
            return;
        }
        $firstRow = reset($rows);
        if (!is_array($firstRow) || empty($firstRow)) {
            throw new InvalidArgumentException("子表数据格式无效");
        }
        // 获取第一个字段作为克隆锚点
        $anchorField = array_key_first($rows[0]);
        $template->cloneRowAndSetValues($anchorField, $rows);
    }
    
    // 处理默认文本值
    private function handleTextValue(TemplateProcessor $template, string $fieldName, $value, array $config)
    {
        $template->setValue($fieldName, $value ? $value : '');
    }
    
    // 更新进度
    private function updateProgress($taskId, $progress) {
        $status = $progress >= 100 ? 'completed' : 'processing';
        $this->db->table('doc_gen_tasks')->where('id', $taskId)->update(['progress' => $progress,'status' => $status]);
    }
    
    // 日志记录
    private function log($message, Throwable $e = null) {
        $logContent = date('[Y-m-d H:i:s] ') . $message;
        if ($e) {
            $logContent .= PHP_EOL . "Stack trace: " . $e->getTraceAsString();
        }
        fwrite(STDERR, $logContent . PHP_EOL);
        fflush(STDERR); // 强制刷新
    }
}